<?php
/**
* 	Bushido
 *
 * @category   Bushido
 * @package    Bushido_DOMElement
 * @copyright  Copyright (c) 2008-2009 Nathan Keyes
 * @author Nathan Keyes
 * @version    $Id: Abstract.php 7 2009-11-14 22:42:08Z nkeyes $
 */


/**
 * 
 * @author Nathan Keyes
 *
 */
abstract class Bushido_DOMElement_Abstract extends Bushido_Object
{
	protected $_allowedAttributes = array();
	protected $_requiredAttributes = array();
	protected $_attributes = array();
	protected $_children = array();
	protected $_allowedChildren = array();
	protected $_requiredChildren = array();
	protected $_text = '';
	
	protected $_xmlWriter;
	
	
	/**
	 * 
	 * @param $allowedAttributes
	 * @param $text
	 * @param $children
	 * @return void
	 */
	public function __construct(array $allowedAttributes = null, string $text = null, array $children = null)
	{
		$this->_setAllowedAttributes();
		$this->_setRequiredAttributes();
		$this->_setAllowedChildren();
		$this->_setRequiredChildren();
	
		
		if($children)
			$this->children = $children;
		
		if($allowedAttributes)
			$this->allowedAttributes = $allowedAttributes;
		
		if($text)
			$this->text = $text;
		
	}
	
	
	/**
	 * 
	 * @return void
	 */
	protected function _initXmlWriter()
	{
		$this->_xmlWriter = new XMLWriter();
		$this->_xmlWriter->openMemory();
		$this->_xmlWriter->setIndent(true);
        $this->_xmlWriter->setIndentString("\t");
	}
	
	/**
	 * 
	 * @return string
	 */
	public function asXML()
	{
		$this-> _initXmlWriter();
		$tagName = $this->_getTagName($this);
		
		
		$this->_xmlWriter->startElement($tagName);
		
		foreach($this->attributes as $attribute => $value)
		{
			if(isset($value))
			{
				$this->_xmlWriter->writeAttribute($attribute, $value);
			}
		}
		
		if($this->text != '')
		{
			$this->_xmlWriter->writeCData($this->text);
		}
		
		foreach($this->children as $child)
		{
			$this->_xmlWriter->writeRaw("$child");
		}
		
		$this->_xmlWriter->endElement();
		return $this->_xmlWriter->outputMemory();
	}
	
	/**
	 * 
	 * @return string
	 */
	public function __toString()
	{
		return $this->asXML();
	}
	
	/**
	 * (non-PHPdoc)
	 * @see library/Bushido/Bushido_Object#__get()
	 */
	public function &__get($name)
	{
		try
		{
			$method = "get" . ucfirst($name);
			if(method_exists($this, $method))
			{
				$method = "get" . ucfirst($name);
				return $this->$method();
			}
			else
			{
				throw new Bushido_Object_Exception("Unknown method '$method' in class " . get_class($this));
			}
		}
		catch(Bushido_Object_Exception $e)
		{
			if(array_key_exists($name, $this->	attributes))
			{
				return $this->_attributes[$name];
			}
			else
			{
				$returnNodes = array();
				
				foreach($this->children as $child)
				{
					$class = "Bushido_DOMElement_" . ucfirst($name);
					if($name == $this->_getTagName($child))
					{
						$returnNodes[] = $child;
					}
				}
				
				if(count($returnNodes) == 0)
				{
					throw new Bushido_Object_Exception("Unknown property '$name' in class " . get_class($this));
				}
				return $returnNodes;
			}
		}
	}
	
	
	/**
	 * (non-PHPdoc)
	 * @see library/Bushido/Bushido_Object#__set()
	 */
	public function __set($name, $value)
	{
		try
		{
			$method = "get" . ucfirst($name);
			if(method_exists($this, $method))
			{
				return $this->$method($value);
			}
			else
			{
				throw new Bushido_Object_Exception("Unknown method '$method' in class '" . get_class($this));
			}
		}
		catch(Bushido_Object_Exception $e)
		{
			if(array_search($name, $this->allowedAttributes))
			{
				return $this->_attributes[$name] = $value;
			}
			else
			{
				throw new Bushido_Object_Exception("Unknown property '$name' in class " . get_class($this) . "' or property is read-only");
			}
		}
	}
	
	/**
	 * 
	 * @return array
	 */
	public function &getChildren()
	{
		return $this->_children;
	}
	
	/**
	 * 
	 * @param $child
	 * @return void
	 */
	public function appendChild(Bushido_DOMElement_Abstract $child)
	{		
		if(in_array($this->_getTagName($child), (array) $this->AllowedChildren))
		{
			parent::appendChild($child);
		}
		else
		{
			
		}
	}
	
	/**
	 * 
	 * @param $children
	 * @return void
	 */
	public function setChildren(array $children)
	{
		foreach($children as $child)
		{
			$this->addChild($child);
		}
	}
	
	
	/**
	 * 
	 * @return array
	 */
	public function &getAttributes()
	{
		return $this->_attributes;
	}
	
	
	/**
	 * 
	 * @param $allowedAttributes
	 * @return void
	 */
	public function setAttributes(array $allowedAttributes)
	{
		foreach($allowedAttributes as $attribute => $value)
		{
			$this->$attribute = $value;
		}
	}
	
	
	public function &getText()
	{
		return $this->_text;
	}
	
	public function setText($text)
	{
		$this->_text = "$text";
	}
	
	public function getAllowedAttributes()
	{
		return Bushido_Utils::sortedKeys($this->_allowedAttributes);
	}
	
	public function getRequiredAttributes()
	{
		return Bushido_Utils::sortedKeys($this->_requiredAttributes);
	}
	
	public function getAllowedChildren()
	{
		return Bushido_Utils::sortedKeys($this->_allowedChildren);
	}
	
	public function getRequiredChildren()
	{
		return Bushido_Utils::sortedKeys($this->_requiredChildren);
	}
	
	protected function _getTagName(Bushido_DOMElement_Abstract $obj)
	{
		$className = get_class($obj);
		$classNameParts = split("_", $className);
		$tagName = end($classNameParts);
		$tagName = strtolower(substr($tagName, 0, 1)) . substr($tagName, 1);
		
		return $tagName;
	}
	
	
	abstract protected function _setAllowedAttributes();
	abstract protected function _setRequiredAttributes();
	abstract protected function _setAllowedChildren();
	abstract protected function _setRequiredChildren();
}
